﻿// Decompiled with JetBrains decompiler
// Type: Netick.Server
// Assembly: Netick, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null
// MVID: 6726ECA1-C773-4CF7-8952-E81B30D93B52
// Assembly location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.dll
// XML documentation location: D:\WorkSpaceDemo\UPM\NetickForUnity\Netick\Runtime\Netick.xml

using System;
using System.Collections.Generic;

#nullable disable
namespace Netick;

public sealed class Server : NetworkPeer
{
  internal List<ServerConnection> Clients;
  internal Queue<ServerConnection> FreeClients;
  internal List<ServerConnection> ConnectedClients;
  internal List<NetworkPlayer> ConnectedPlayers;
  private NetworkConnectionRequest _cachedRequest = new NetworkConnectionRequest();
  private List<ServerConnection> _pendingKickedClients;
  private float _inKBps;
  private float _outKBps;

  public override float InKBps => this._inKBps;

  public override float OutKBps => this._outKBps;

  internal Server(NetickEngine engine)
    : base(engine)
  {
    int maxClients = engine.MaxClients;
    this.ConnectedPlayers = new List<NetworkPlayer>(engine.MaxPlayers);
    this.ConnectedClients = new List<ServerConnection>(maxClients);
    this.Clients = new List<ServerConnection>(maxClients);
    this.FreeClients = new Queue<ServerConnection>(maxClients);
    this._pendingKickedClients = new List<ServerConnection>(maxClients);
    for (short clientIndex = 0; (int) clientIndex < maxClients; ++clientIndex)
      this.Clients.Add(new ServerConnection(clientIndex, this.Engine));
    for (short index = 0; (int) index < maxClients; ++index)
      this.FreeClients.Enqueue(this.Clients[(int) index]);
  }

  internal override void Start(int port)
  {
    if (this.Engine.IsLoggingEnabled)
      NetickLogger.Log(this.Engine, this.Engine.StartMode == NetickStartMode.SinglePlayer ? (object) "Started." : (object) $"Started on {port}.");
    if ((this.Engine.StartMode == NetickStartMode.Host || this.Engine.StartMode == NetickStartMode.SinglePlayer) && !this.ConnectedPlayers.Contains((NetworkPlayer) this))
      this.ConnectedPlayers.Add((NetworkPlayer) this);
    this.Transport?.Run(RunMode.Server, port);
  }

  internal override void Stop()
  {
    foreach (ServerConnection serverConnection in this.ConnectedClients.ToArray())
    {
      this.InternalOnDisconnected(serverConnection.TransportConnection, TransportDisconnectReason.Shutdown, false, true);
      this.Transport.Disconnect(serverConnection.TransportConnection);
    }
    this._pendingKickedClients.Clear();
    this.Transport?.ForceUpdate();
    base.Stop();
  }

  internal override void Update()
  {
    this._inKBps = 0.0f;
    this._outKBps = 0.0f;
    for (int index = 0; index < this._pendingKickedClients.Count; ++index)
    {
      ServerConnection pendingKickedClient = this._pendingKickedClients[index];
      if (this.ConnectedClients.Contains(pendingKickedClient))
      {
        this.OnDisconnected(pendingKickedClient.TransportConnection, TransportDisconnectReason.Kick);
        this.Transport.Disconnect(pendingKickedClient.TransportConnection);
      }
    }
    float rawDeltaTime = this.Engine.Timer.RawDeltaTime;
    if (!this.Engine._simulateClientsMode)
    {
      for (int index = 0; index < this.ConnectedClients.Count; ++index)
      {
        this.ConnectedClients[index].Update(rawDeltaTime);
        this._inKBps += this.ConnectedClients[index].BytesIn.AvgKBps;
        this._outKBps += this.ConnectedClients[index].BytesOut.AvgKBps;
      }
    }
    else
    {
      for (int index = 0; index < this.Clients.Count; ++index)
      {
        this.Clients[index].Update(rawDeltaTime);
        this._inKBps += this.Clients[index].BytesIn.AvgKBps;
        this._outKBps += this.Clients[index].BytesOut.AvgKBps;
      }
    }
  }

  public void Kick(ServerConnection client) => this._pendingKickedClients.Add(client);

  internal ServerConnection GetClientByIndex(int index) => this.Clients[index];

  public override void OnConnectFailed(ConnectionFailedReason reason)
  {
  }

  public override bool OnConnectRequest(byte[] data, int length, IEndPoint source)
  {
    if (this.ConnectedClients.Count >= this.Engine.MaxClients)
      return false;
    this._cachedRequest.AllowConnection = true;
    this._cachedRequest.Source = source;
    this._cachedRequest.Data = data;
    this._cachedRequest.DataLength = length;
    try
    {
      this.Engine._gameEngine.OnConnectRequest(this._cachedRequest);
    }
    catch (Exception ex)
    {
      NetickLogger.LogError(this.Engine, (object) ex);
    }
    this._cachedRequest.Source = (IEndPoint) null;
    return this._cachedRequest.AllowConnection;
  }

  public override void OnConnected(TransportConnection peer)
  {
    if (this.ConnectedClients.Count >= this.Engine.MaxClients)
    {
      this.Transport.Disconnect(peer);
      if (!this.Engine.IsLoggingEnabled)
        return;
      NetickLogger.Log(this.Engine, (object) $"Client {peer.EndPoint.ToString()} was disconnected due to exceeding MaxClients count.");
    }
    else
    {
      ServerConnection serverConnection = this.FreeClients.Dequeue();
      peer.AssociatedNetworkConnection = (NetworkConnection) serverConnection;
      serverConnection.TransportConnection = peer;
      if (this.Engine.IsLoggingEnabled)
        NetickLogger.Log(this.Engine, (object) $"Client {serverConnection.TransportConnection.EndPoint.ToString()} connected.");
      this.ConnectedClients.Add(serverConnection);
      this.ConnectedPlayers.Add((NetworkPlayer) serverConnection);
      serverConnection.OnConnected(peer);
      try
      {
        this.Engine._gameEngine.OnPeerConnected((NetworkConnection) serverConnection);
        this.Engine._gameEngine.OnPlayerConnected((NetworkPlayer) serverConnection);
        this.Engine._playersReplicator.OnPlayerConnected((NetworkPlayer) serverConnection);
      }
      catch (Exception ex)
      {
        NetickLogger.LogError(this.Engine, (object) ex);
      }
    }
  }

  public override void OnDisconnected(
    TransportConnection peer,
    TransportDisconnectReason transportDisconnectReason)
  {
    this.InternalOnDisconnected(peer, transportDisconnectReason, true, false);
  }

  internal void InternalOnDisconnected(
    TransportConnection peer,
    TransportDisconnectReason transportDisconnectReason,
    bool callOnInputSourceLeft,
    bool isLocalShutdown)
  {
    if (peer.AssociatedNetworkConnection == null)
      return;
    ServerConnection networkConnection = peer.AssociatedNetworkConnection as ServerConnection;
    if (!this.ConnectedClients.Contains(networkConnection))
      return;
    if (this.Engine.IsLoggingEnabled)
      NetickLogger.Log(this.Engine, (object) $"Client {networkConnection.TransportConnection.EndPoint.ToString()} disconnected.");
    this._pendingKickedClients.Remove(networkConnection);
    this.ConnectedClients.Remove(networkConnection);
    this.ConnectedPlayers.Remove((NetworkPlayer) networkConnection);
    this.FreeClients.Enqueue(networkConnection);
    if (!isLocalShutdown)
    {
      try
      {
        this.Engine._gameEngine.OnPeerDisconnected((NetworkConnection) networkConnection, transportDisconnectReason);
        this.Engine._gameEngine.OnPlayerDisconnected((NetworkPlayer) networkConnection, transportDisconnectReason);
        this.Engine._playersReplicator.OnPlayerDisconnected((NetworkPlayer) networkConnection);
      }
      catch (Exception ex)
      {
        NetickLogger.LogError(this.Engine, (object) ex);
      }
    }
    networkConnection.OnDisconnected(transportDisconnectReason, callOnInputSourceLeft);
  }

  public override void Receive(
    TransportConnection connection,
    BitBuffer buffer,
    TransportDeliveryMethod transportDeliveryMethod)
  {
    if ((double) this.Engine.SimulatedLossIn > 0.0 && this.Engine._rand.NextDouble() < (double) this.Engine.SimulatedLossIn)
      return;
    try
    {
      this.Engine.CurrentRpcSource = connection.AssociatedNetworkConnection;
      connection.AssociatedNetworkConnection?.ReadPacket(buffer, transportDeliveryMethod);
    }
    catch (Exception ex)
    {
      if (this.Engine.IsLoggingEnabled)
        NetickLogger.LogError(this.Engine, (object) $"Kicked client {connection.EndPoint} for bad packet. {ex}");
      this.Engine.Server.Kick((ServerConnection) connection.AssociatedNetworkConnection);
    }
    this.Engine.CurrentRpcSource = (NetworkConnection) null;
    buffer.Clear();
  }
}
